Android窗口管理分析(三):WMS窗口的组织形式

Android窗口管理分析(三):WMS窗口的组织形式

本文总共包括以下几点:

  • 窗口的分组管理:应用窗口组、子窗口组、系统窗口组
  • Activity、Dialg应用窗口及PopWindow子窗口的添加原理跟注意事项
  • 窗口的Z次序管理:窗口的分配序号、次序调整等
  • WMS中窗口次序分配如何影响SurfaceFlinger服务

各种窗口type和窗口类型的对应关系是什么样的?

窗口和对应类型

窗口type值 窗口类型
FIRST_APPLICATION_WINDOW=1 开始应用程序窗口
TYPE_BASE_APPLICATION=1 所有程序窗口的base窗口,其他应用程序都显示在它上面
TYPE_APPLICATION=2 普通应用程序窗口,token必须设置为Activity的token
TYPE_APPLICATION_STARTING=3 应用程序启动时所显示的窗口
LAST_APPLICATION_WINDOW=99 结束应用程序窗口

一般Activity都是TYPE_BASE_APPLICATION类型的,而TYPE_APPLICATION主要是用于Dialog,再看下子窗口类型

子窗口和对应类型

窗口type值 窗口类型
FIRST_SUB_WINDOW=1000 SubWindows子窗口,子窗口的Z序和坐标空间
TYPE_APPLICATION_PANEL =1000 面板窗口,显示于宿主窗口的上层
TYPE_APPLICATION_MEDIA=1001 媒体窗口(例如视频),显示于宿主窗口下层
TYPE_APPLICATION_SUB_PANEL=1002 应用程序窗口的子面板,显示于所有面板窗口的上层
TYPE_APPLICATION_ATTACHED_DIALOG=1003 对话框,类似于面板窗口,显示于所有面板窗口的上层
TYPE_APPLICATION_MEDIA_OVERLAY=1004 媒体信息,显示在媒体层和程序窗口之间,需要实现半透明效果
LAST_SUB_WINDOW=1999 结束子窗口

系统窗口类型

窗口type值 窗口类型
FIRST_SYSTEM_WINDOW=2000 系统窗口
TYPE_STATUS_BAR=FIRST_SYSTEM_WINDOW 状态栏
TYPE_SYSTEM_ALERT=FIRST_SYSTEM_WINDOW+3 系统提示,出现在应用程序窗口之上
TYPE_TOAST=FIRST_SYSTEM_WINDOW+5 显示Toast

窗口的分组原理是什么样的?

Android的窗口是以token来分组的

windowToken包含一个WindowList,包含一系列的WindowState

windowtoken

Activity对应token和WindowToken的添加过程是什么样的?

  1. AMS将Activity的token加入WMS中,为Activity创建APPWindowToken。
  2. Activity分组在Activity显示之前就被AMS添加到WMS中,之后AMS才会去通知App新建Activity,并将Activity的Window添加到WMS中。
  3. 启动Activity步骤:新建一个Activity,并为Activity创建一个appContext,这个Context主要是为了activity.attach使用的,其实是单纯new一个ContextImpl,之后Activity会利用attach函数将ContextImpl绑定在自己身上。

创建ActivityContext

  1. 为Activity绑定ContextImpl,因为Activity只是一个ContextWrapper。
  2. new一个PhoneWindow并设置回调。
  3. 利用当前的WindowManagerImpl为Window创建一个WindowManagerImpl并设置它的parentWindow。

setWindowManager

  1. 将window的WindowManager传递给Activity,作为Activity的WindowManager
  2. Activity通过getSystemService获取WindowManager服务时,直接返回了Window的WindowManagerImpl

getSystemService

  1. 通过需要获得的service名字返回不同manager对象。

handleResumeActivity

  1. 获取activity的window对象,如果用户没有通过setContentView方式新建DecorView,这里会利用PhoneWindow的getDecorView()新建DecorView,并把decorview设置为不可见。
  2. 获取windowManager以及window的各属性。添加decorview到WMS管理。

WindowManagerImpl的addView

  1. 最终会调用WindowManagerGlobal的addView方法
  2. 调整wparams的token参数
  3. 新建ViewRootImpl,并利用wparams参数添加窗口
  4. ViewRootImpl设置view

adjustLayoutParamsForSubWindow

  1. 对于Activity来说,wp.token = mContainer其实是AMS端传过来的IApplicationToken
  2. 在ViewRootImpl中setView的时候,利用IWindowSession代理与WMS端的Session通信,将窗口及token信息传递到WMS端,其中IApplicationToken就是该Activity处于的分组
  3. 在WMS端,根据IApplicationToken IBinder键值,从全局的mTokenMap。

Dialog必须用Activity的context?

在添加到WMS时,Dialog窗口属性是WindowManager.LayoutParams.TYPE_APPLICATION,同样属于应用窗口,所以必须使用Activity的AppToken才可以。

Dialog和Activity共享一个同一个WindowManager(也就是WindowManagerImpl),而WindowManagerImpl里面有个Window类型的mParentWindow变量,这个变量在attach时传入当前Activity的Window。而Activity的Window里面的mAppToken是当前Activity的token。

所以最终是因为不能为Dialog提供正确的token

PopupWindow的子窗口添加流程是什么样的?

WMS为PopupWindow窗口创建一个子窗口分组WindowToken,每个子窗口都会有一个指向父窗口的引用,因为是利用父窗口IWindow作为键值,父窗口可以方便利用IWindow获取WindowToken,存入map中,进而得到全部的子窗口。

窗口的Z次序管理是什么样的?

Z序列坐标系

在WMS中,窗口被抽象成WindowState,采用了三个int值来标志窗口所在位置。前两个主要根据窗口类型确定窗口位置,mLayer才是真正的值。

1
2
3
4
5
6
7
8
9
final class WindowState implements WindowManagerPolicy.WindowState {

final WindowList mChildWindows = new WindowList();
final int mBaseLayer;
final int mSubLayer;
<!--最终Z次序的赋值-->
int mLayer;

}

由于前两个都是final修饰的,所以只有最后一个值可以改。

从坐标系知道,值越大,窗口越靠上。

对于Activity等应用窗口来说,主序都是一样的,怎么定他们真正的Z-order呢?其实是Activity的顺序由AMS来保证的。

在系统层面,决定了不同类型窗口所处的位置,比如系统Toast类型的窗口一定处于所有应用窗口之上

添加窗口的示意代码如下

1
2
3
4
5
6
7
8
addWindow(){
<!--1-->
new WindowState
<!--2-->
addWindowToListInOrderLocked(win, true);
<!--3-->
assignLayersLocked(displayContent.getWindowList());
}

Z-order通过addWindowToListInOrderLocked及assignLayersLocked才能确定

  1. 第一步,new一个windowstate实例。

  2. 第二步,addWindowToListInOrderLocked主要根据窗口的Token找到归属,插入到对应Token的WindowState列表,插入到特定位置后,Z-order就确定了。

  3. 第三步,通过assignLayersLocked为WindowState分配真正的Z-order mLayer。

当mLayer(int)确定后,这个顺序最终确定,之后,在SurfaceFlinger图层混排的时候处理。

WMS中窗口次序分配如何影响SurfaceFlinger服务?

SurfaceFlinger图层混排的时候不会混排所有的窗口,只会混排可见的窗口,比如有多个全屏Activity的时候,SurfaceFlinger只会处理最上面的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
SurfaceControl.openTransaction();
try {
mSurfaceX = left;
mSurfaceY = top;
try {
mSurfaceControl.setPosition(left, top);
mSurfaceLayer = mAnimLayer;
final DisplayContent displayContent = w.getDisplayContent();
if (displayContent != null) {
mSurfaceControl.setLayerStack(displayContent.getDisplay().getLayerStack());
}
<!--设置次序-->
mSurfaceControl.setLayer(mAnimLayer);
mSurfaceControl.setAlpha(0);
mSurfaceShown = false;
} catch (RuntimeException e) {
mService.reclaimSomeSurfaceMemoryLocked(this, "create-init", true);
}
mLastHidden = true;
} finally {
SurfaceControl.closeTransaction();
}
}

通过事务来确保完整性,包括surface次序调整,通知SurfaceFlinger更新Surface信息。